﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using TransportLayer.NodeToolsLibrary;

namespace TransportLayer.NodeLibrary
{
    public class SinkSNP : Cleaner
    {
        private Sink node;
        
        private Socket managerSocket;
        private byte[] buffer = new byte[4086];

        public SinkSNP(Sink n, int port)
        {
            try
            {
                node = n;

                managerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint server = new IPEndPoint(IPAddress.Loopback, port);
                managerSocket.Connect(server);
                managerSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnDataReceived, managerSocket);
            }
            catch (Exception) { }
        }

        private void OnDataReceived(IAsyncResult asyn)
        {
            int recv;
            try { recv = managerSocket.EndReceive(asyn); }
            catch (SocketException) { managerSocket.Close(); return; }
            if (recv == 0)
            {
                managerSocket.Close();
                return;
            }
            string query = Encoding.ASCII.GetString(buffer, 0, recv);
            //prepere response
            string response = ProcessQuery(query);
            if (response != "")
                managerSocket.Send(System.Text.Encoding.ASCII.GetBytes(response));
                managerSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnDataReceived, managerSocket);
          
        }
        private string rsp = "ping";
        public string ProcessQuery(string query)
        {
            query = query.ToLower();
            string[] command = query.Split(' ');
            string response = "";
            if (command[0] == "ping")
            {
                response += "ping";
            }
            else if (command[0] == "get")
            {
                if (command[1] == "log" && command.Length == 3)
                {
                    int n;
                    try { n = Int32.Parse(command[2]); }
                    catch (FormatException) { n = 0; }
                    if (n == 0)
                        return Serial.SerializeObject(node.Log);
                    else
                        return Serial.SerializeObject(new Log(node.Log, n));
                }
                if (command.Length != 2)
                    return response;

                if (command[1] == "routing")
                    return "getrsp " + GetRoutingTable();
                response += "getresp " + command[1];
                string[] param = command[1].Split('.');
                if (param[0] == "type")
                    response += " Sink";
                if (param[0] == "id")
                    response += " " + node.Id;
                else if (param[0] == "name")
                    response += " " + node.Name;
                else if (param[0] == "portsin")
                {
                    if (param.Length != 3 && param.Length != 5)
                        return response;
                    if (param[2] == "open")
                        response += " " + node.PortIn.Open;
                    else if (param[2] == "connected")
                        response += " " + node.PortIn.Connected;
                    else if (param[2] == "_port")
                        response += " " + node.PortIn.TcpPort;
                    else if (param[2] == "vailable")
                    {
                        try
                        {
                            int stream = Int32.Parse(param[1]);
                            int port = Int32.Parse(param[3]);
                          
                            response += " " + CheckPortIn(stream, port);
                        }
                        catch (FormatException) { return response; }
                    }
                }
            }
            else if (command[0] == "set")
            {
                if (command.Length != 3)
                    return response;
                response += "setresp " + command[1];
                string[] param = command[1].Split('.');
                if (param[0] == "id")
                    response += " " + node.Id; // parametr niezmienny
                else if (param[0] == "name")
                {
                    node.Name = command[2];
                    response += " " + node.Name;
                }
                else if (param[0] == "portsin")
                {
                    if (param.Length != 3)
                        return response;
                    if (param[2] == "open")
                        response += " " + node.PortIn.Open; // póki co niezmienne
                    else if (param[2] == "connected")
                        response += " " + node.PortIn.Connected; // niezmienne
                    else if (param[2] == "_port")
                        response += " " + node.PortIn.TcpPort; // niezmienne
                    node.Log.LogMsg(response);
                }
            }
            else if (command[0] == "rtadd")
            {
                response += "rtaddresp ";
                if (command.Length == 3)
                {
                    try
                    {
                        RoutingEntry incoming = new RoutingEntry(command[1]);
                        int connectionID = Int32.Parse(command[2]);

                        if (1 > incoming.Port && node.Receiver.Add(incoming, connectionID))
                        {
                            response += "ok";
                        }
                        else
                            response += "fail";
                    }
                    catch (FormatException) { response += "fail"; }
                    catch (ArgumentException) { response += "fail"; }
                }
                else { response += "fail"; }
            }
            else if (command[0] == "rtdel")
            {
                response += "rtdelresp ";
                if (command.Length != 2)
                    return response;
                
                try
                {
                    if (node.Receiver.Remove(Int32.Parse(command[1])))
                        response += "ok";
                    else
                        response += "fail";
                }
                catch (FormatException) { response += " fail"; }
            }
            else
                response = command[0] + "resp";
            return response;
        }

        private string GetRoutingTable()
        {
            string table = "";
            foreach (var element in node.Receiver.Sources)
            {
                table += "[" +element.Value+ "] " + element.Key.ToString() +  "\n";
            }
            return table;
        }

        private bool CheckPortIn(int stream,int port)
        {
            return !node.Receiver.Sources.ContainsKey(new RoutingEntry(stream,port));
        }

        public void Clean()
        {
            managerSocket.Disconnect(true);
            managerSocket.Close();
        
        }
    }
}

